package Service

import (
	"TCPServer/Interfaces"
	"bufio"
	"fmt"
	"io"
	"net"
)

type Connection struct {
	Conn         *net.TCPConn
	ConnID       uint16
	isClosed     bool
	Router       Interfaces.IMessageHandler
	ExitBuffChan chan bool
	msgChan      chan []byte
}

func NewConnection(conn *net.TCPConn, connID uint16, router Interfaces.IMessageHandler) *Connection {
	c := &Connection{
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		Router:       router,
		ExitBuffChan: make(chan bool, 1),
		msgChan:      make(chan []byte),
	}
	return c
}

func (c *Connection) Start() {
	go c.StartReader()
	go c.StartWriter()
	for {
		select {
		case <-c.ExitBuffChan:
			return
		}
	}
}

func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running")
	defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
	defer c.Stop()
	reader := bufio.NewReader(c.GetTCPConnection())
	for {
		peek, err := reader.Peek(6)
		if err != nil {
			if err == io.EOF {
				c.ExitBuffChan <- true
				break
			}
			continue
		}
		i32 := uint32(peek[2]) | uint32(peek[3])<<8 | uint32(peek[4])<<16 | uint32(peek[5])<<24
		if uint32(reader.Buffered()) < i32+6 {
			continue
		}
		i16 := uint16(peek[0]) | uint16(peek[1])<<8
		reader.Discard(6)

		dataByte := make([]byte, i32)
		_, err = reader.Read(dataByte)
		if err != nil {
			c.ExitBuffChan <- true
			break
		}

		msg := NewMessage(i16, dataByte)
		req := &Request{
			conn: c,
			msg:  msg,
		}
		// c.Router.SendMsgToTaskQueue(req)
		go c.Router.DoMsgHandler(req)
	}
}

func (c *Connection) StartWriter() {
	for {
		select {
		case data := <-c.msgChan:
			if _, err := c.GetTCPConnection().Write(data); err != nil {
				fmt.Println("Send Data error:, ", err, " Conn Writer exit")
				return
			}
		case <-c.ExitBuffChan:
			return
		}
	}
}

func (c *Connection) Stop() {
	if c.isClosed == true {
		return
	}
	c.isClosed = true
	c.Conn.Close()
	c.ExitBuffChan <- true
	close(c.ExitBuffChan)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnID() uint16 {
	return c.ConnID
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

func (c *Connection) SendMsg(data []byte) {
	c.msgChan <- data
}
